/*
File: rentalApp.cpp
Authors: Team 3- Sam Stewart, Dylan McDougall, Stephen Ten Eyck
Date: 06/11/12
CSCE 315- 100 Summer 1 2012
*/

#include "std_lib_facilities.h"
#include "dbms.h"

/*
Table format:
customers.db
UserID, firstName, lastName, phoneNumber,

movies.db
inventoryNumber, dvdID, movieTitle,

rentals.db
userID, inventoryNumber, checkOutDate, checkInDate,
*/

/*-----------------------------GLOBAL FUNCTIONS------------------------*/

//check if char is a digit
bool digitCheck(char c){
    if((int)c < 58 && (int)c > 47){
        return true;
    }
    else return false;
}

//used after a choice is made to save and reload tables
void resetDB(DBMS &d){
	d.write(d.getTable("Customers"));
	d.write(d.getTable("Movies"));
	d.write(d.getTable("Rentals"));
	d.clearTables();
	d.read("Movies.db");
	d.read("Customers.db");
	d.read("Rentals.db");
}

/*-----------------------------MAIN APPLICATION------------------------*/

int main(){
	DBMS rentalDB;
	int choiceInt = 0;							//user menu input
	string choiceString = "";					//user menu input as a string
	int movieID = 1;						 	//unique to movie title
	int userID = 1; 							//unique to each user
	int inventoryNum = 1;						//unique to each movie, regardless of title
	
	//load tables
	rentalDB.read("Movies.db");
	rentalDB.read("Customers.db");
	rentalDB.read("Rentals.db");
			
	//set user id
	if(rentalDB.getTable("Customers")->numTuples() > 0){
		string str = rentalDB.getTable("Customers")->getValue(rentalDB.getTable("Customers")->numTuples()-1, 0);
		userID = atoi(str.c_str()) + 1;
	}
	
	//set movie id
	int maxIDInt = 0;
	string maxIDString;
	for(int i=0; i<rentalDB.getTable("Movies")->numTuples(); i++){
		maxIDString = rentalDB.getTable("Movies")->getValue(i, 1);
		maxIDInt = atoi(maxIDString.c_str());
		if(maxIDInt >= movieID){
			movieID = maxIDInt + 1;
		}
	}
	
	//set inventory #
	if(rentalDB.getTable("Movies")->numTuples() > 0){
		string str = rentalDB.getTable("Movies")->getValue(rentalDB.getTable("Movies")->numTuples()-1, 0);
		inventoryNum = atoi(str.c_str()) + 1;
	}
	
	//welcome message
	cout << "\n\n\n"
			 << "*************************************************\n"
			 << "*                                               *\n"
			 << "*      Welcome to the DVD RENTAL SYSTEM         *\n"
			 << "*                                               *\n"
			 << "*************************************************\n\n\n"
			 << "*****  What would you like to do?  **************\n"; 
	
	
	//Main loop
	do{
		cout << "*****  Please choose from the menu below:  ******\n"
			 << "*****  Enter 18 if you would like to EXIT  ******\n\n\n"
			 << "Enter 1 if you would like to add a customer.\n" 
			 << "Enter 2 if you would like to add a new DVD.\n"
			 << "Enter 3 if you would like to remove a customer.\n"
			 << "Enter 4 if you would like to remove a DVD.\n"
			 << "Enter 5 if you would like to update one of a customer's attributes.\n"
			 << "Enter 6 if you would like to update one of a DVD's attributes.\n"
			 << "Enter 7 if you would like to see a list of all customers in the database.\n"
			 << "Enter 8 if you would like to search for a certain customer by name.\n"
			 << "Enter 9 if you would like to search for a certain customer by phone number.\n"
			 << "Enter 10 if you would like to see a list of all DVDs in the database.\n"
			 << "Enter 11 if you would like to search for a certain DVD by ID number.\n"
			 << "Enter 12 if you would like to search for a certain DVD by title.\n"
			 << "Enter 13 if you would like to search DVDs by inventory number.\n"
			 << "Enter 14 if you would like to check out a DVD.\n"
			 << "Enter 15 if you would like to check in a DVD.\n"
			 << "Enter 16 if you would like to show rental list by customer.\n"
			 << "Enter 17 if you would like to show rental list by DVD-ID.\n\n\n>";
			
		getline(cin, choiceString);
		//check if integer is an integer
		if(!integerCheck(choiceString)){
			choiceInt = 19;
		}
		else{
			choiceInt = atoi(choiceString.c_str());
		}
		
		switch (choiceInt){
			case 1:{//Add a customer
					cout << "Please enter the following information.\n\n"
						 << "Enter the customer's first name: ";
					string fname, lname, phoneNum;
					getline(cin, fname);
					cout << "\nEnter the customer's last name: ";
					getline(cin, lname);
					cout << "\nEnter the customer's phone number ((###)###-####): ";
					getline(cin, phoneNum);
					
					//check phone # format
					if(phoneNum.size() != 13){
						cout << "Invalid Phone # format.\n";
						break;
					}
					else if(phoneNum.at(0) == '(' && digitCheck(phoneNum.at(1))
							&& digitCheck(phoneNum.at(2)) && digitCheck(phoneNum.at(3))
							&& phoneNum.at(4) == ')' && digitCheck(phoneNum.at(5))
							&& digitCheck(phoneNum.at(6)) && digitCheck(phoneNum.at(7))
							&& phoneNum.at(8) == '-' && digitCheck(phoneNum.at(9))
							&& digitCheck(phoneNum.at(10)) && digitCheck(phoneNum.at(11))
							&& digitCheck(phoneNum.at(12))){
							//end phone # format check
						
						//create tuple 						
						vector<string> newTuple;
						//convert int to string
						stringstream ss;
						ss << userID;
						string userIDString = ss.str();
						newTuple.push_back(userIDString);
						newTuple.push_back(fname);
						newTuple.push_back(lname);
						newTuple.push_back(phoneNum);
						rentalDB.addTuple(rentalDB.getTable("Customers"), newTuple);
						//keep user id unique
						userID++;
					}
					else{
						cout << "Invalid Phone # format.\n";
					}
					resetDB(rentalDB);
				}
				break;
			case 2:{//Add a DVD
					string dvdTitle;
					cout << "Please enter the title of the DVD: ";
					int tempDVDID = movieID;
					getline(cin,dvdTitle);
					
					//set inventory and DVDid numbers
					for(int i=0; i<rentalDB.getTable("Movies")->numTuples(); i++){
						if(dvdTitle == rentalDB.getTable("Movies")->getValue(i,2)){
							string str = rentalDB.getTable("Movies")->getValue(i,1);
							tempDVDID = atoi(str.c_str());
						}
					}
					if(tempDVDID == movieID){
						movieID++;
					}
					
					//add DVD to table
					vector<string> newTuple;
					//convert int to string
					stringstream ss;
					ss << inventoryNum;
					newTuple.push_back(ss.str());
					inventoryNum++;
					ss.str("");
					ss << tempDVDID;
					newTuple.push_back(ss.str());
					newTuple.push_back(dvdTitle);
					rentalDB.addTuple(rentalDB.getTable("Movies"), newTuple);
					cout << "Movie succesfully added.\n\n";
					resetDB(rentalDB);
				}
				break;
			case 3:{//Remove a customer
					//show Customer list in order to see UserID
					string userIDString;
					rentalDB.show(rentalDB.getTable("Customers"));
					cout << "\nPlease enter the UserID# of the customer you would like to remove: ";
					getline(cin, userIDString);
					
					vector<string> tempKey;
					tempKey.push_back(userIDString);
					bool deleteIt = true;				//used to check for errors
					
					//check if delete possible
					for(int i=0; i<rentalDB.getTable("Rentals")->numTuples();i++){
						if(tempKey == rentalDB.getTable("Rentals")->getKeyFromTuple(i)){
							cout << "\nCannot delete because customer has movie checked out! \n\n";
							deleteIt = false;
						}
					}
					
					//delete if check is true
					if(deleteIt){
						cout << "Removing user ID: " << userIDString << " from the system.\n\n";
						rentalDB.deleteTuple(rentalDB.getTable("Customers"), tempKey);
					}
					resetDB(rentalDB);
				}
				break;
			case 4:{//Remove a DVD
					string inventoryNumString;
					rentalDB.show(rentalDB.getTable("Movies"));
					cout << "\nPlease enter the Inventory# of the DVD you would like to remove: ";
					getline(cin, inventoryNumString);
					
					vector<string> tempKey;
					tempKey.push_back(inventoryNumString);
					
					//check Rentals table to make sure Invertory# is not checked out
					bool deleteIt = true;
					for(int i=0; i<rentalDB.getTable("Rentals")->numTuples();i++){
						if(tempKey == rentalDB.getTable("Rentals")->getKeyFromTuple(i)){
							cout << "\nCannot delete because movie has been checked out!\n\n";
							deleteIt = false;
						}
					}
					
					//delete if check was true
					if(deleteIt){
						cout << "Removing Inventory #: " << inventoryNumString << " from the system.\n\n";
						rentalDB.deleteTuple(rentalDB.getTable("Movies"), tempKey);
					}
					resetDB(rentalDB);
				}
				break;
			case 5:{//Update customer's attribute
					//show Customer list in order to see UserID
					string userIDString;
					string tempUserChoice;
					rentalDB.show(rentalDB.getTable("Customers"));
					cout << "\nPlease enter the following information.\n\n"
						 << "Please enter the UserID# of the customer you would like to update: ";
					getline(cin, userIDString);
					
					//check to make sure its an integer
					if(!integerCheck(userIDString)){
						cout << "Needs to be an integer value.\n";
						break;
					}
					
					cout << "\nPlease enter the category you would like to change: \n"
						 << "1: First Name\n"
						 << "2: Last Name\n"
						 << "3: Phone Number\n"
						 << "4: Cancel\n";
						 
					getline(cin, tempUserChoice);
					int attrListToUpdate = -1;
					if(tempUserChoice == "1"){
						attrListToUpdate = 1;
					}
					else if(tempUserChoice == "2"){
						attrListToUpdate = 2;
					}
					else if(tempUserChoice == "3"){
						attrListToUpdate = 3;
					}
					else if(tempUserChoice == "4"){
						break;
					}
					else{
						cout << "Bad input.\n\n";
						break;
					}
					
					cout << "\nPlease enter the new attribute: ";
					string attrNew;
					getline(cin, attrNew);
					string oldAttr = "";
				
					//Update attribute here
					vector<string> tempKey;
					tempKey.push_back(userIDString);
					
					for(int i=0; i<rentalDB.getTable("Customers")->numTuples(); i++){
						if(userIDString == rentalDB.getTable("Customers")->getValue(i, 0)){
							oldAttr = rentalDB.getTable("Customers")->getValue(i, attrListToUpdate);
						}
					}
					if(oldAttr != ""){//success
						rentalDB.updateAttr(rentalDB.getTable("Customers"), tempKey, oldAttr, attrNew);
						cout << "Attribute Updated\n"; 
					}
					else{
						cout << "Customer or attribute not found.\n\n";
					}
					resetDB(rentalDB);
				}
				break;
			case 6:{//Update DVD's attribute
					//show Movie list in order to see UserID
					string movieIDString;
					string titleOld = "", titleNew;
					rentalDB.show(rentalDB.getTable("Movies"));
					cout << "\nPlease enter the following information.\n\n"
						 << "Please enter the MovieID# of the Movie you would like to update: ";
					getline(cin, movieIDString);
					cout << "Please enter the new title name: ";
					getline(cin, titleNew);
					
					//check to make sure it is an integer
					if(!integerCheck(movieIDString)){
						cout << "Needs to be an integer value.\n";
						break;
					}
					
					vector<string> tempKey;
					vector< vector<string> > tempKeys;	//vector of keys
					
					for(int i=0; i<rentalDB.getTable("Movies")->numTuples(); i++){
						if(movieIDString == rentalDB.getTable("Movies")->getValue(i, 1)){
							tempKey.push_back(rentalDB.getTable("Movies")->getValue(i, 0));
							tempKeys.push_back(tempKey);
							tempKey.clear();
							titleOld = rentalDB.getTable("Movies")->getValue(i, 2);
						}
					}
					for(int i=0; i<tempKeys.size(); i++){
						rentalDB.updateAttr(rentalDB.getTable("Movies"), tempKeys[i], titleOld, titleNew);
					}
					if(titleOld == ""){
						cout << "Movie does not exist.\n\n";
					}
					resetDB(rentalDB);
				}
				break;
			case 7:{//show all customers
					cout << "Here is a list of all customers.\n";
					rentalDB.show(rentalDB.getTable("Customers"));
					resetDB(rentalDB);
				}
				break;
			case 8:{//search cust by last name
					string lname;
					cout << "\nEnter the customer's last name: ";
					getline(cin, lname);
					
					//search by name means select
					vector<string> cond;
					cond.push_back(lname);
					cond.push_back("==");
					cond.push_back("Last Name");
					Table tempTable = rentalDB.select(rentalDB.getTable("Customers"), cond);
					tempTable.print();
					resetDB(rentalDB);
				}
				break;
			case 9:{//Search customers by phone
					string phoneNum;
					cout << "\nPlease enter the customer's phone number ((###)###-####) you are searching for: ";
					getline(cin, phoneNum);
					//check phone # format
					if(phoneNum.size() != 13){
						cout << "Invalid Phone # format.\n";
						break;
					}
					else if(phoneNum.at(0) == '(' && digitCheck(phoneNum.at(1))
							&& digitCheck(phoneNum.at(2)) && digitCheck(phoneNum.at(3))
							&& phoneNum.at(4) == ')' && digitCheck(phoneNum.at(5))
							&& digitCheck(phoneNum.at(6)) && digitCheck(phoneNum.at(7))
							&& phoneNum.at(8) == '-' && digitCheck(phoneNum.at(9))
							&& digitCheck(phoneNum.at(10)) && digitCheck(phoneNum.at(11))
							&& digitCheck(phoneNum.at(12))){//end phone # format check
						
						//search by phone means select
						vector<string> cond;
						cond.push_back(phoneNum);
						cond.push_back("==");
						cond.push_back("Phone #");
						Table tempTable = rentalDB.select(rentalDB.getTable("Customers"), cond);
						tempTable.print();
					}
					else{
						cout << "Invalid Phone # format.\n";
					}
					resetDB(rentalDB);
				}
				break;
			case 10:{//Show all DVDs
					cout << "Here is a list of all DVDs available.\n";
					rentalDB.show(rentalDB.getTable("Movies"));
					resetDB(rentalDB);
				}
				break;
			case 11:{//Search DVDs by ID#
					cout << "Please enter the DVD ID# you are searching for: ";
					string movieIDString;
					getline(cin, movieIDString);
					
					//search by dvd id means select
					vector<string> cond;
					cond.push_back("DVD ID");
					cond.push_back("==");
					cond.push_back(movieIDString);
					Table tempTable = rentalDB.select(rentalDB.getTable("Movies"), cond);
					tempTable.updateName("Movies");
					tempTable.print();
					resetDB(rentalDB);
				}
				break;
			case 12:{//Search DVDs by Title
					string dvdTitle;
					cout << "Please enter the title of the DVD you are searching for: ";
					getline(cin, dvdTitle);
					
					//search by title means select
					vector<string> cond;
					cond.push_back("Movie Title");
					cond.push_back("==");
					cond.push_back(dvdTitle);
					Table tempTable = rentalDB.select(rentalDB.getTable("Movies"), cond);
					tempTable.updateName("Movies");
					tempTable.print();
					resetDB(rentalDB);
				}
				break;
			case 13:{//Search DVDs by Inventory #
					cout << "Please enter the DVDs inventory# you are searching for: ";
					string inventoryNumString;
					getline(cin, inventoryNumString);
					
					//search by inventory number means select
					vector<string> cond;
					cond.push_back("Inventory #");
					cond.push_back("==");
					cond.push_back(inventoryNumString);
					Table tempTable = rentalDB.select(rentalDB.getTable("Movies"), cond);
					tempTable.updateName("Movies");
					tempTable.print();
					resetDB(rentalDB);
				}
				break;
			case 14:{//Check out a DVD
					//print dvd table
					rentalDB.show(rentalDB.getTable("Movies"));
					
					string inventoryNumString;
					string userIDString;
					string checkOutDate;
					string dueDate;
					bool validDate = true;
					cout << "What is the inventory number of the DVD to be checked out: ";
					getline(cin, inventoryNumString);
					vector<string> tempKey;
					tempKey.push_back(inventoryNumString);
					
					//check to make sure inventoryNum is available
					for(int i=0; i<rentalDB.getTable("Rentals")->numTuples(); i++){
						if(tempKey == rentalDB.getTable("Rentals")->getKeyFromTuple(i)){
							cout << "\nMovie is already checked out! \n";
							break;
						}
					}
					
					cout << "\nWhat is the User ID# of the customer renting: ";
					//show customers
					rentalDB.show(rentalDB.getTable("Customers"));
					getline(cin, userIDString);
					
					cout << "\nEnter today's date (MM/DD/YY): ";
					getline(cin, checkOutDate);
					
					//check if valid fromat
					if(!(digitCheck(checkOutDate.at(0)) && digitCheck(checkOutDate.at(1))
							&& checkOutDate.at(2) == '/' && digitCheck(checkOutDate.at(3))
							&& digitCheck(checkOutDate.at(4)) && checkOutDate.at(5) == '/'
							&& digitCheck(checkOutDate.at(6)) && digitCheck(checkOutDate.at(7)))){
						validDate = false;
						cout << "Invalid check out date fromat.\n";
					}
					
					cout << "\nEnter the due date (MM/DD/YY): ";
					getline(cin, dueDate);
					
					//check if valid fromat
					if(!(digitCheck(dueDate.at(0)) && digitCheck(dueDate.at(1))
							&& dueDate.at(2) == '/' && digitCheck(dueDate.at(3))
							&& digitCheck(dueDate.at(4)) && dueDate.at(5) == '/'
							&& digitCheck(dueDate.at(6)) && digitCheck(dueDate.at(7)))){
						validDate = false;
						cout << "Invalid due date fromat.\n";
					}
					
					if(validDate){
						//creating tuple
						vector<string> tempTuple;
						tempTuple.push_back(userIDString);
						tempTuple.push_back(inventoryNumString);
						tempTuple.push_back(checkOutDate);
						tempTuple.push_back(dueDate);
						
						//adding tuple to Rental table
						rentalDB.addTuple(rentalDB.getTable("Rentals"), tempTuple);
					}
					resetDB(rentalDB);
				}
				break;
			case 15:{//Check in a DVD
					string inventoryNumString;
					string userIDString;
					rentalDB.show(rentalDB.getTable("Rentals"));
					cout << "What is the inventory number of the DVD to be checked in: ";
					getline(cin, inventoryNumString);
					cout << "\nWhat is the User ID# of the customer returning: ";
					getline(cin, userIDString);
					bool checkOutSuccess = false;
					
					vector<string> tempKey;
					tempKey.push_back(inventoryNumString);
					
					//check to make sure inventoryNum is in Rentals database and matches UserID#
					for(int i=0; i<rentalDB.getTable("Rentals")->numTuples(); i++){
						if(tempKey == rentalDB.getTable("Rentals")->getKeyFromTuple(i)){//match inventory number in table
							//remove tuple from Rentals table
							if(userIDString == rentalDB.getTable("Rentals")->getValue(i, "User ID ")){
								cout << "Inventory # matches User ID, checking in..." << endl;
								rentalDB.getTable("Rentals")->deleteTuple(tempKey);
								checkOutSuccess = true;
							}
						}
					}
					if(!checkOutSuccess){
						cout << "Entry not found.\n\n";
					}
					resetDB(rentalDB);
				}
				break;
			case 16:{//Show rentals by customer
					cout << "Here is the list of rentals by customer.\n\n";
					
					//cross all tables
					Table *tempTable = new Table(rentalDB.crossProduct(rentalDB.getTable("Customers"), rentalDB.getTable("Rentals")));	//cust * rent
					Table *tempTable2 = new Table(rentalDB.crossProduct(tempTable, rentalDB.getTable("Movies")));						//cust * rent * movies
					
					//select matching IDs
					vector<string> cond;
					cond.push_back("User ID");
					cond.push_back("==");
					cond.push_back("User ID ");
					Table *tempTable3 = new Table(rentalDB.select(tempTable2, cond));

					//project important attributes
					vector<string> attrs;
					attrs.push_back("First Name");
					attrs.push_back("Last Name");
					attrs.push_back("Due Date");
					attrs.push_back("Movie Title");
					attrs.push_back("User ID");
					attrs.push_back("User ID ");
					attrs.push_back("Inventory #");
					attrs.push_back("Inventory # ");
					Table *tempTable4 = new Table(rentalDB.project(tempTable3, attrs));
					
					//select matching inventory #s
					vector<string> cond3;
					cond3.push_back("Inventory #");
					cond3.push_back("==");
					cond3.push_back("Inventory # ");
					Table *tempTable5 = new Table(rentalDB.select(tempTable4, cond3));
					
					//project most important attrs
					attrs.clear();
					attrs.push_back("First Name");
					attrs.push_back("Last Name");
					attrs.push_back("Due Date");
					attrs.push_back("Movie Title");
					Table *tempTable6 = new Table(rentalDB.project(tempTable5, attrs));
					tempTable6->updateName("Rentals");
					
					//search by customer last name means select
					string lname;
					cout << "Enter the customer's last name.\n";
					cond.clear();
					getline(cin, lname);
					cond.push_back(lname);
					cond.push_back("==");
					cond.push_back("Last Name");
					Table tempTable7 = rentalDB.select(tempTable6, cond);
					tempTable7.print();
					
					//deallocate memory
					delete tempTable;
					delete tempTable2;
					delete tempTable3;
					delete tempTable4;
					delete tempTable5;
					delete tempTable6;
					
					resetDB(rentalDB);
				}
				break;
			case 17:{//Show rentals by DVD
					cout << "Here is the list of rentals by DVD ID#.\n\n";
					
					//cross all tables
					Table *tempTable = new Table(rentalDB.crossProduct(rentalDB.getTable("Customers"), rentalDB.getTable("Rentals")));	//cust * rent
					Table *tempTable2 = new Table(rentalDB.crossProduct(tempTable, rentalDB.getTable("Movies")));						//cust * rent * movies
					
					//select matching IDs
					vector<string> cond;
					cond.push_back("User ID");
					cond.push_back("==");
					cond.push_back("User ID ");
					Table *tempTable3 = new Table(rentalDB.select(tempTable2, cond));

					//project important attributes
					vector<string> attrs;
					attrs.push_back("DVD ID");
					attrs.push_back("First Name");
					attrs.push_back("Last Name");
					attrs.push_back("Due Date");
					attrs.push_back("Movie Title");
					attrs.push_back("User ID");
					attrs.push_back("User ID ");
					attrs.push_back("Inventory #");
					attrs.push_back("Inventory # ");
					Table *tempTable4 = new Table(rentalDB.project(tempTable3, attrs));
					
					//select matching inventory #s
					vector<string> cond3;
					cond3.push_back("Inventory #");
					cond3.push_back("==");
					cond3.push_back("Inventory # ");
					Table *tempTable5 = new Table(rentalDB.select(tempTable4, cond3));
					
					//project most important attrs
					attrs.clear();
					attrs.push_back("DVD ID");
					attrs.push_back("First Name");
					attrs.push_back("Last Name");
					attrs.push_back("Due Date");
					attrs.push_back("Movie Title");
					Table *tempTable6 = new Table(rentalDB.project(tempTable5, attrs));
					tempTable6->updateName("Rentals");
					
					//search by DVD ID means select
					string DVDID;
					cout << "Enter the DVD ID.\n";
					cond.clear();
					getline(cin, DVDID);
					cond.push_back(DVDID);
					cond.push_back("==");
					cond.push_back("DVD ID");
					Table tempTable7 = rentalDB.select(tempTable6, cond);
					tempTable7.print();
					
					//deallocate memory
					delete tempTable;
					delete tempTable2;
					delete tempTable3;
					delete tempTable4;
					delete tempTable5;
					delete tempTable6;
					
					resetDB(rentalDB);
				}
				break;
			case 18:{
					resetDB(rentalDB);	//save tables
					cout << "Exiting system.\n";
				}
				break;
			default:
				cout << "That is not one of the choices. Please enter one of the options.\n\n";
		}
		cin.sync();				//clear cin stream
	}while (choiceInt != 18);
	return 0;
}
